package com.luych.toolbox.common.xml;

import org.w3c.dom.Document;
import org.w3c.dom.NamedNodeMap;
import org.w3c.dom.Node;
import org.w3c.dom.NodeList;
import org.xml.sax.SAXException;

import javax.xml.XMLConstants;
import javax.xml.parsers.DocumentBuilder;
import javax.xml.parsers.DocumentBuilderFactory;
import javax.xml.parsers.ParserConfigurationException;
import java.io.ByteArrayInputStream;
import java.io.IOException;

public class Xml2XmlNode {

    private final String xml;

    public Xml2XmlNode(String xml) {
        this.xml = xml.replaceAll("[\\x00-\\x08\\x0b-\\x0c\\x0e-\\x1f]", "");
    }

    private boolean isText(Node node) {
        return node.getChildNodes().getLength() == 1 &&
                node.getChildNodes().item(0).getClass().getName().equals("com.sun.org.apache.xerces.internal.dom.DeferredTextImpl");
    }

    private boolean isNegligible(Node node) {
        return node.getClass().getName().equals("com.sun.org.apache.xerces.internal.dom.DeferredTextImpl") &&
                node.getNodeName().equals("#text");
    }

    private XmlNode convert(Node node) {
        XmlNode thisResult = new XmlNode(node.getNodeName());
        NamedNodeMap attributes = node.getAttributes();
        if (attributes != null) {
            for (int i = 0; i < attributes.getLength(); i++) {
                Node attribute = attributes.item(i);
                thisResult.getAttrs().put(attribute.getNodeName(), attribute.getNodeValue());
            }
        }
        NodeList childNodes = node.getChildNodes();
        if (childNodes != null) {
            if (this.isText(node)) {
                thisResult.setValue(childNodes.item(0).getTextContent());
            } else {
                for (int i = 0; i < childNodes.getLength(); i++) {
                    Node child = childNodes.item(i);
                    if (this.isNegligible(child)) {
                        continue;
                    }
                    XmlNode childResult = this.convert(child);
                    thisResult.getChildren().add(childResult);
                    childResult.setParent(thisResult);
                }
            }
        }
        return thisResult;
    }

    public XmlNode convert() {
        try {
            DocumentBuilderFactory factory = DocumentBuilderFactory.newInstance();
            factory.setExpandEntityReferences(false);
            factory.setFeature(XMLConstants.FEATURE_SECURE_PROCESSING, true);
            DocumentBuilder builder = factory.newDocumentBuilder();
            Document document = builder.parse(new ByteArrayInputStream(xml.getBytes()));
            return this.convert(document.getDocumentElement());
        } catch (ParserConfigurationException | SAXException | IOException e) {
            throw new RuntimeException(e);
        }
    }


}
